package com.greate.community.service;

import com.greate.community.dao.FollowMapper;
import com.greate.community.entity.Follow;
import com.greate.community.entity.User;
import com.greate.community.constant.CommunityConstant;
import com.greate.community.util.RedisKeyUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.dao.DataAccessException;
import org.springframework.data.redis.core.RedisOperations;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.SessionCallback;
import org.springframework.stereotype.Service;

import java.util.*;

/**
 * 关注相关
 */
@Slf4j
@Service
public class FollowService implements CommunityConstant {

    @Autowired
    private RedisTemplate redisTemplate;

    @Autowired
    private UserService userService;

    @Autowired
    private FollowMapper followMapper;

    /**
     * 关注或取关
     *
     * @param userId     执行操作的用户id
     * @param entityType 关注的实体类型，3-用户
     * @param entityId   关注的实体id
     */
    public void followOrUnfollow(int userId, int entityType, int entityId) {
        boolean hasFollowed = hasFollowed(userId, entityType, entityId);

        //先对mysql进行处理
        if (hasFollowed) {
            // 如果用户已经关注该实体，点第二次则取消关注
            followMapper.deleteFollowByUserIdAndEntity(userId, entityType, entityId);
        } else {
            Follow follow = new Follow();
            follow.setUserId(userId);
            follow.setEntityType(entityType);
            follow.setEntityId(entityId);
            follow.setCreateTime(new Date());
            followMapper.insertFollow(follow);
        }

        //再对redis进行处理
        redisTemplate.execute(new SessionCallback() {
            @Override
            public Object execute(RedisOperations redisOperations) throws DataAccessException {
                // 生成 Redis 的 key
                String followeeKey = RedisKeyUtil.getFolloweeKey(userId, entityType);
                String followerKey = RedisKeyUtil.getFollowerKey(entityType, entityId);

                redisOperations.multi(); // 开启事务

                if (hasFollowed) {
                    // 如果用户已经关注该实体，点第二次则取消关注
                    // 删除数据
                    redisOperations.opsForZSet().remove(followeeKey, entityId);
                    redisOperations.opsForZSet().remove(followerKey, userId);
                } else {
                    // 插入数据
                    long currentTimeMillis = System.currentTimeMillis();
                    redisOperations.opsForZSet().add(followeeKey, entityId, currentTimeMillis);
                    redisOperations.opsForZSet().add(followerKey, userId, currentTimeMillis);
                }
                return redisOperations.exec(); // 提交事务
            }
        });
    }

    /**
     * 查询某个用户关注的某类型实体的数量
     *
     * @param userId     用户 id
     * @param entityType 实体类型
     * @return
     */
    public long findFolloweeCount(int userId, int entityType) {
        String followeeKey = RedisKeyUtil.getFolloweeKey(userId, entityType);
        long count = 0;

        if (redisTemplate.hasKey(followeeKey)) {
            // 先在 redis 中查询
            Long num = redisTemplate.opsForZSet().zCard(followeeKey);
            count = num == null ? 0 : num;
        } else {
            // redis中没有，则在 mysql 中查询
            List<Follow> followeeList = followMapper.selectRowsByUserIdAndEntityType(userId, entityType);
            count = followeeList.size();
            // 找到了之后存入 redis
            for (Follow follow : followeeList) {
                redisTemplate.opsForZSet().add(followeeKey, follow.getEntityId(), follow.getCreateTime().getTime());
            }
        }
        return count;
    }

    /**
     * 查询某个实体的粉丝数量
     *
     * @param entityType
     * @param entityId
     * @return
     */
    public long findFollowerCount(int entityType, int entityId) {
        String followerKey = RedisKeyUtil.getFollowerKey(entityType, entityId);
        long count = 0;

        if (redisTemplate.hasKey(followerKey)) {
            // 先在 redis 中查询
            Long num = redisTemplate.opsForZSet().zCard(followerKey);
            count = num == null ? 0 : num;
        } else {
            // redis中没有，则在 mysql 中查询
            List<Follow> followerList = followMapper.selectRowsByEntityTypeAndEntityId(entityType, entityId);
            count = followerList.size();
            // 找到了之后存入 redis
            for (Follow follow : followerList) {
                redisTemplate.opsForZSet().add(followerKey, follow.getUserId(), follow.getCreateTime().getTime());
            }
        }
        return count;
    }

    /**
     * 判断当前用户是否已关注该实体
     *
     * @param userId
     * @param entityType
     * @param entityId
     * @return
     */
    public boolean hasFollowed(int userId, int entityType, int entityId) {
        String followeeKey = RedisKeyUtil.getFolloweeKey(userId, entityType);
        boolean hasFollowed = false;
        if (redisTemplate.hasKey(followeeKey)) {
            // 先在 redis 中查询
            hasFollowed = redisTemplate.opsForZSet().score(followeeKey, entityId) != null;
        } else {
            // redis中没有，则在 mysql 中查询
            Date createTime = followMapper.selectByUserIdAndEntity(userId, entityType, entityId);
            if (createTime != null) {
                hasFollowed = true;
                // 找到了之后存入 redis
                redisTemplate.opsForZSet().add(followeeKey, entityId, createTime.getTime());
            }

        }
        return hasFollowed;
    }

    /**
     * 查询用户关注某实体的关注时间，如果还未关注，返回 null 值
     *
     * @param userId
     * @param entityType
     * @param entityId
     * @return
     */
    public Date findFollowTime(int userId, int entityType, int entityId) {

        String followeeKey = RedisKeyUtil.getFolloweeKey(userId, entityType);
        Date followTime = null;
        if (redisTemplate.hasKey(followeeKey)) {
            // 先在 redis 中查询
            Double score = redisTemplate.opsForZSet().score(followeeKey, entityId);
            followTime = new Date(score.longValue());
        } else {
            // redis中没有，则在 mysql 中查询
            Date createTime = followMapper.selectByUserIdAndEntity(userId, entityType, entityId);
            if (createTime != null) {
                followTime = createTime;
            }
        }
        return followTime;
    }

    /**
     * 分页查询某个用户关注的人
     *
     * @param userId
     * @param offset
     * @param limit
     * @return
     */
    public List<Map<String, Object>> findFollowees(int userId, int offset, int limit) {
        String followeeKey = RedisKeyUtil.getFolloweeKey(userId, ENTITY_TYPE_USER);
        //先查 redis
        Set<Integer> targetIds = redisTemplate.opsForZSet().reverseRange(followeeKey, offset, offset + limit - 1);
        if (targetIds == null || targetIds.isEmpty()) {
            // redis 中没有，查 mysql
            List<Follow> followeeUsers = followMapper.selectFollowees(userId, offset, limit);
            if (followeeUsers == null || followeeUsers.size() == 0) {
                return null;
            } else {
                List<Map<String, Object>> list = new ArrayList<>();
                followeeUsers.forEach(follow -> {
                    Map<String, Object> map = new HashMap<>();
                    map.put("user", userService.findUserById(follow.getEntityId()));
                    map.put("followTime", follow.getCreateTime());
                    list.add(map);
                });
                return list;
            }
        } else {
            // redis 中有，则直接查 redis
            List<Map<String, Object>> list = new ArrayList<>();
            targetIds.forEach(id -> {
                Map<String, Object> map = new HashMap<>();
                User user = userService.findUserById(id);
                map.put("user", user);
                Date followTime = findFollowTime(userId, ENTITY_TYPE_USER, id);
                map.put("followTime", followTime);
                list.add(map);
            });
            return list;
        }
    }

    /**
     * 分页查询某个用户的粉丝
     *
     * @param userId
     * @param offset
     * @param limit
     * @return
     */
    public List<Map<String, Object>> findFollowers(int userId, int offset, int limit) {
        String followerKey = RedisKeyUtil.getFollowerKey(ENTITY_TYPE_USER, userId);
        //先查 redis
        Set<Integer> targetIds = redisTemplate.opsForZSet().reverseRange(followerKey, offset, offset + limit - 1);
        if (targetIds == null || targetIds.isEmpty()) {
            // redis 中没有，查 mysql
            List<Follow> followerUsers = followMapper.selectFollowers(userId, offset, limit);
            if (followerUsers == null || followerUsers.size() == 0) {
                return null;
            } else {
                List<Map<String, Object>> list = new ArrayList<>();
                followerUsers.forEach(follow -> {
                    Map<String, Object> map = new HashMap<>();
                    map.put("user", userService.findUserById(follow.getUserId()));
                    map.put("followTime", follow.getCreateTime());
                    list.add(map);
                });
                return list;
            }
        } else {
            // redis 中有，则直接查 redis
            List<Map<String, Object>> list = new ArrayList<>();
            targetIds.forEach(id -> {
                Map<String, Object> map = new HashMap<>();
                User user = userService.findUserById(id);
                map.put("user", user);
                Date followTime = findFollowTime(id, ENTITY_TYPE_USER, userId);
                map.put("followTime", followTime);
                list.add(map);
            });
            return list;
        }
    }
}
